import {
	str2ab
} from '../util'

// 默认的回调模板
const mommonCallback = (msg) => {
	return (res) => {
		console.log(msg, res)
	}
}
// 一次性写入的指令
const ONE_TIME_CMD = ['f2', 'f3']

// 蓝牙连接整体流程类
// 先理清蓝牙整个流程是怎样的：
// 1.蓝牙适配器初始化 -> 2.判断手机是否打开蓝牙，即蓝牙是否可用 -> 3.搜索周围蓝牙设备 -> 4.获取目标设备（两种形式，获取目前搜索到的全部，或者监听新设备发现事件） -> 
// 5.连接目标设备 -> 6.获取连接设备的所有service服务 -> 7.获取某个service的characteristic特征值 -> 8.启用notify -> 9.监听设备特征值变化 -> 10.写入指令
// 其中ios中写指令必须在获取了serviceId和特征值之后才行，所以统一在初始化的时候走完流程再写指令

export class BleManager {
	adapterState = false // 手机蓝牙适配器当前状态
	foundDevices = [] // 当前搜索到的设备
	connectedDevice = null // 当前连接的设备
	services = [] // 当前连接设备的serviceId
	notifyServiceId = '' // 当前设备类型对应的具有读、写、通知属性的服务uuid
	characteristics = [] // 当前连接设备的characteristics特征值
	deviceFoundFilter = null // 发现设备时的筛选过滤器
	callback = {}
	constructor(Device) { // 构造函数内初始化各自设备的各种回调
		// 都传一个默认值
		this.notifyServiceId = Device.notifyServiceId || ''
		this.deviceFoundFilter = Device.deviceFoundFilter || (() => true) // 默认不过滤
		this.callback = {
			// 这里在传构造参数Device的回调函数时要改变其this指向该类
			connectionStateChange: Device.connectionStateChange.bind(this) || mommonCallback('连接状态改变：'),
			characteristicValueChange: Device.characteristicValueChange.bind(this) || mommonCallback('特征值改变：'),
			adapterStateChange: (state) => {
				if (!this.adapterState) { // 如果已经拿过适配器状态就不再判断了
					if (state.available) {
						wx.showToast({
							title: '蓝牙已打开!',
							duration: 1000,
							mask: true,
						})
						this.adapterState = state.available;
					} else {
						wx.showModal({
							title: '蓝牙未开启',
							content: '请前往手机设置打开蓝牙',
							showCancel: false,
							confirmText: '知道了',
						})
					}
				}
			}
		}
	}

	// 初始化
	init() {
		this.bleReset();
		this.AdapterInit()
	}
	// 蓝牙重置，主要是关闭一些timer
	bleReset() {
		console.log('重置')
		this.adapterState = false
		this.foundDevices = []
		this.connectedDevice = null
		this.services = []
		this.characteristics = []
	}
	// 先初始化蓝牙适配器
	AdapterInit() {
		wx.openBluetoothAdapter({
			success: (res) => {
				console.log('蓝牙适配器初始化成功: ', res)
				this.getAdapterState()
			},
			fail: (err) => { // 初始化失败，除了可能是没开蓝牙，还可能是用户的手机需要给微信授权蓝牙功能，这可以通过err的state状态为3或4来判断
				console.log('蓝牙初始化错误，3未授权，4未开启', err.state)
				if (err.state === 3 || err.state === 4) {
					wx.showModal({
						title: '蓝牙未授权',
						content: '请前往手机-设置-隐私-蓝牙，对您的微信授权蓝牙功能',
						showCancel: false,
						confirmText: '知道了',
					})
				} else {
					wx.showModal({
						title: '蓝牙未开启',
						content: '请前往手机设置打开蓝牙',
						showCancel: false,
						confirmText: '知道了',
					})
				}
			}
		})
	}
	// 获取本机蓝牙适配器状态
	getAdapterState() {
		wx.getBluetoothAdapterState({
			success: (res) => this.adapterState = res.available,
			fail: (err) => console.log('获取蓝牙状态失败：', err),
		})
	}
	// 开始搜索设备
	startDevicesDiscovery() {
		wx.startBluetoothDevicesDiscovery({
			allowDuplicatesKey: false,
			success: (res) => {
				// 成功启动后开始监听新设备发现
				this.onDeviceFound()
			},
			fail: (err) => {
				console.log('搜索设备启用失败: ', err)
			}
		})
	}
	// 停止搜索设备
	stopDevicesDiscovery() {
		wx.stopBluetoothDevicesDiscovery({
			success: (res) => {
				// 关闭监听新设备发现
				wx.offBluetoothDeviceFound()
			},
			fail: (err) => {
				console.log('搜索设备停止失败: ', err)
			}
		})
	}

	// 监听新设备发现
	onDeviceFound() {
		uni.onBluetoothDeviceFound(res => {
			uni.getBluetoothDevices({
				success: res => {
					this.foundDevices = res.devices.filter((item) =>
						this
						.deviceFoundFilter(item))
					console.log('res.devices', res.devices, this.foundDevices)

					// this.foundDevices = this.foundDevices.concat(res.devices.filter((item) => this
					// 	.deviceFoundFilter(item) && (this.foundDevices.indexOf(item) == -1)))
				}
			})
		})
		// wx.onBluetoothDeviceFound((res) => {
		// 	this.foundDevices = this.foundDevices.concat(res.devices.filter((item) => {
		// 		return this.deviceFoundFilter(item) && (this.foundDevices.indexOf(item) == -1)
		// 	}))
		// })
	}
	// 连接设备
	connectToDevice(targetDevice) {
		return new Promise((resolve, reject) => {
			// 先停止搜索
			this.stopDevicesDiscovery()
			wx.showLoading({
				title: '正在连接...',
				mask: true
			})
			wx.createBLEConnection({
				deviceId: targetDevice.deviceId,
				success: () => {
					wx.hideLoading()
					// wx.showToast({
					// 	title: '连接成功',
					// 	duration: 1500,
					// 	icon: 'success'
					// })
					this.connectedDevice = targetDevice
					// 连接成功后保存设备，然后初始化蓝牙事件
					this.initBleListeners().then(() => {
						resolve()
					})
					/* 设置一次能接受到最大数据值，默认只有20 */
					setTimeout(() => {
						wx.setBLEMTU({
							deviceId: this.connectedDevice.deviceId,
							mtu: 250,
							success(res) {
								console.log('设置成功', res)
							},
							fail(err) {
								console.log('设置失败', err)
							}
						})
					}, 1000)
				},
				fail: (err) => {
					wx.hideLoading()
					console.log('连接设备失败：', err)
					if (err.errCode == -1 || err.errCode == 0) {
						// wx.showToast({
						// 	title: '连接成功',
						// 	duration: 1500,
						// 	icon: 'success'
						// })
						this.connectedDevice = targetDevice
						// 连接成功后保存设备，然后初始化蓝牙事件
						this.initBleListeners().then(() => {
							resolve()
						})
					} else {
						wx.showToast({
							title: '连接失败',
							duration: 1500,
							icon: 'error'
						})
					}

				}
			})
		})
	}
	// 断开连接设备
	disconnectToDevice() {
		if (!!this.connectedDevice) {
			wx.closeBLEConnection({
				deviceId: this.connectedDevice.deviceId,
				success: () => {
					this.connectedDevice = null
					this.services = []
					this.characteristics = []
				},
				fail: (res) => {
					console.log('失败了', res, this.connectedDevice)
				},
				complete: () => {
					if (this.adapterState) {
						wx.closeBluetoothAdapter()
					}
					//this.bleReset();
				}
			});

		} else {
			if (this.adapterState) {
				wx.closeBluetoothAdapter()
			}
			//this.bleReset()
		}

	}
	// 初始化蓝牙事件，包括监听蓝牙连接状态，监听适配器状态，nofity启用
	initBleListeners() {
		return new Promise((resolve, reject) => {
			// 注册连接状态改变的回调
			wx.onBLEConnectionStateChange(this.callback.connectionStateChange)
			// 注册适配器状态改变的回调
			wx.onBluetoothAdapterStateChange(this.callback.adapterStateChange)
			// 启用notify
			this.enableNotify().then(() => {
				resolve()
			})
		})
	}
	// 启用notify，启用之前需要先获取服务id和对应服务id的特征值
	enableNotify() {
		return new Promise((resolve, reject) => {
			this.getServiceId().then(() => {
				for (let i = 0; i < this.notifyServiceId.length; i++) {
					this.getCharacteristic(this.notifyServiceId[i]).then(() => {
						// 可以开始启用notify了
						if (this.notifyServiceId[i] == '0000FFE0-0000-1000-8000-00805F9B34FB') {
							this.notifyBLECharacteristic(this.notifyServiceId[i]).then(() => {
								resolve()
							})
						} else {
							resolve()
						}
					})
				}
			})
		})
	}
	// 正式启用notify
	notifyBLECharacteristic(serviceId) {
		return new Promise((resolve, reject) => {
			let characteristicId = this.characteristics.find(item => item.type === 'notify' && item
				.serviceId == serviceId).uuid;
			wx.notifyBLECharacteristicValueChange({
				deviceId: this.connectedDevice.deviceId,
				serviceId: serviceId,
				characteristicId,
				state: true, // 启用选项，必填
				type: 'notification', // 这里默认值是indication，有些设备不填notification就会出错，虽然启用成功，但是不能监听回调
				success: () => {
					console.log('notify启用成功', serviceId, characteristicId)
					// 然后开启监听特征值变化
					wx.onBLECharacteristicValueChange(this.callback.characteristicValueChange)
					resolve()
				},
				fail: (err) => {
					console.log('notify启用失败：', err)
				}
			})
		})
	}
	//获取设备服务ID
	getServiceId() {
		return new Promise((resolve, reject) => {
			wx.getBLEDeviceServices({
				deviceId: this.connectedDevice.deviceId,
				success: (res) => {
					console.log('获取到设备服务id', res.services)
					this.services = res.services
					resolve()
				},
				fail: (err) => {
					console.log('获取到设备服务id失败：', err)
				}
			})
		})
	}
	// 获取设备特征值
	getCharacteristic(serviceId) {
		console.log("到这里获取设备特征值-服务Id",serviceId)
		console.log("连接的设备Id",this.connectedDevice.deviceId)
		return new Promise((resolve, reject) => {
			wx.getBLEDeviceCharacteristics({
				deviceId: this.connectedDevice.deviceId,
				serviceId: serviceId,
				success: (res) => {
					console.log("获取设备特征值",res)
					res.characteristics.forEach(item => {
						// 保存读、写和notify特征值
						if (item.properties.write) {
							this.characteristics.push({
								type: 'write',
								uuid: item.uuid,
								serviceId
							})
						}
						if (item.properties.read) {
							this.characteristics.push({
								type: 'read',
								uuid: item.uuid,
								serviceId
							})
						}
						if (item.properties.notify) {
							this.characteristics.push({
								type: 'notify',
								uuid: item.uuid,
								serviceId
							})
						}
					})
					console.log('获取到设备特征值', this.characteristics)
					resolve()
				},
				fail: (err) => {
					console.log("错误信息",JSON.stringify(err))
				}
			})
		})
	}
	// 对连接设备写入指令, cmd是传入的不带0x头的16进制string指令
	writeValue(cmd) {
		// 由于写指令会有包限制的情况，如果超过了最大包限制，设备是接收不到指令的，所以要么分包处理，要么wx.setBLEMTU()设置包大小
		// 这里我们分包处理，默认情况下，一般都是一包23字节，其中3字节是通讯包头，所以我们传的指令包只能有20字节
		// 部分指令必须一次性写入，比如ER2的读文件就是一次性
		// 转换为ArrayBuffer


		let serviceId = '0000FFE5-0000-1000-8000-00805F9B34FB';
		let characteristicId='';
		console.log("到这里乐乐乐=》"+JSON.stringify(this.characteristics))
		if(this.characteristics&&this.characteristics.length>0){
			characteristicId = this.characteristics.find(item => item.type === 'write' && item
				.serviceId == serviceId).uuid;
		}
		/* let characteristicId = this.characteristics.find(item => item.type === 'write' && item
			.serviceId == serviceId).uuid; */
		console.log('characteristicId', characteristicId)
		if (ONE_TIME_CMD.indexOf(this.cmdRecord) > -1) {
			wx.writeBLECharacteristicValue({
				deviceId: this.connectedDevice.deviceId,
				serviceId,
				characteristicId,
				// 有些蓝牙sdk不需要转ArrayBuffer，但是大部分都需要
				value: str2ab(cmd),
				success: () => {
					console.log('写指令成功：', cmd)
				},
				error: (err) => {
					console.error('写入指令失败：', err)
					console.error('当前指令：', cmd)
				}
			})
		} else {
			for (let i = 0; i < cmd.length; i += 40) {
				wx.writeBLECharacteristicValue({
					deviceId: this.connectedDevice.deviceId,
					serviceId,
					characteristicId,
					// 有些蓝牙sdk不需要转ArrayBuffer，但是大部分都需要
					value: str2ab(cmd.substring(i, i + 40)),
					success: () => {
						console.log('写指令成功：', cmd)
					},
					error: (err) => {
						console.error('写入指令失败：', err)
						console.error('当前指令：', cmd)
					}
				})
			}
		}
	}
}